package remato.client.model;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;

import org.eclipse.core.runtime.IStatus;

import remato.client.controller.Assert;
import remato.client.controller.RequirementPlugin;
import remato.client.controller.ModelStatus;
import remato.client.model.facade.DNDTarget;
import remato.client.model.facade.INodeChild;
import remato.client.model.facade.INodeParent;
import remato.client.model.facade.IPropertiesContainer;
import remato.client.view.util.ReflectionHelper;
import remato.common.domain.helpers.Nameable;
import remato.common.domain.helpers.Parentable;

import com.wgo.bpot.common.removeme.Persistent;

/**
 * 
 * 
 * @author Petter L. H. Eide
 * @version $Id: NodeParent.java,v 1.4 2006-02-19 16:29:37 petterei Exp $
 */
public abstract class NodeParent<T extends INodeParent, S extends INodeChild> extends NodeChild<T> implements INodeParent<T,S>, DNDTarget {

	private static final String HAS_CHILDREN_METHOD_NAME = "hasChildren";
	
    private Map<Persistent, S> children = new Hashtable<Persistent, S>();
    
    private Method hasChildrenMethod = null;
    private Method[] getChildrenMethods = null;
    
    protected NodeParent(Parentable nameable, Class parentInterface, String parentCoreMethodName, String[] getChildrenMethodNames) {
        super(nameable, parentInterface, parentCoreMethodName);
    	initMethods(nameable, getChildrenMethodNames);
    }
    protected NodeParent(Nameable nameable, T parent, Class parentInterface, String parentCoreMethodName, String[] getChildrenMethodNames){
    	super(nameable, parent, parentInterface, parentCoreMethodName);
    	initMethods(nameable, getChildrenMethodNames);
    }
	
    private void initMethods(Persistent parent, String[] getChildrenMethodNames ) {
    	Class<? extends Persistent> targetClass = parent.getClass(); 
    	hasChildrenMethod = ReflectionHelper.retrieveMethod("Could not retrieve hasChildren method", targetClass, HAS_CHILDREN_METHOD_NAME);
    	getChildrenMethods = new Method[getChildrenMethodNames.length];
    	for (int i = getChildrenMethodNames.length; --i >= 0;) {
    		 getChildrenMethods[i] = ReflectionHelper.retrieveMethod("Could not retrieve getChildren method, " + getChildrenMethodNames[i] + " for " + getDomainObjectType(), targetClass, getChildrenMethodNames[i]);
    	}
    }
    
	/**
	 * @see remato.client.model.facade.INodeParent#getChildren()
	 */
	@SuppressWarnings("unchecked")
	public final Collection<S> getChildren() {
		Map<Persistent, S> newChildren = new Hashtable<Persistent, S>();
		Persistent domainModelObject = getDomainModelObject();
		synchronized (children) {
			for (Method getChildrenMethod : getChildrenMethods) {
			for (Persistent childPersistent : ReflectionHelper.<Collection<Persistent>>invoke("Could not get system children.", domainModelObject, getChildrenMethod)) {
				S nodeChild = children.remove(childPersistent);
	        	if (null != nodeChild) {
	        		newChildren.put(childPersistent, nodeChild);
	        	} else {
	        		newChildren.put(childPersistent, (S)RequirementPlugin.getInstance().getSession().getWrapperFactory().getWrapper(childPersistent, this));
	        	}
	        }
			}
	        for (S child : children.values()) {
	        	RequirementPlugin.log(new ModelStatus(IStatus.WARNING, getClass().getSimpleName() + "Inconsistent children collection: " + child + " not removed."));
	        }
	        children = newChildren;
		}
		return children.values();	
	}
	
	/**
	 * @see remato.client.model.facade.INodeParent#hasChildren()
	 */
	public final boolean hasChildren() {
		Persistent domainModelObject = getDomainModelObject();
		boolean hasChildren = ReflectionHelper.<Boolean>invoke("Could not check if system has children.", domainModelObject, hasChildrenMethod).booleanValue();
		if ( ! hasChildren && 0 < children.size() ) {
			Assert.warningIfFalse(getChildren().size() == 0, getClass().getSimpleName() + ": Inconsistent childrens hierarchy.."); // last check to ensure wysiwyg if save does work.. and triger children set update in gui..
		}
		return hasChildren;
		
//		if ( ReflectionHelper.<Boolean>invoke("Could not check if system has children.", domainModelObject, hasChildrenMethod).booleanValue() ) {
//			return true;
//		}
//		boolean hasChildren = false;
//		for (Method getChildrenMethod : getChildrenMethods) {
//			hasChildren = ReflectionHelper.<Collection<Persistent>>invoke("Could not get system children.", domainModelObject, getChildrenMethod).size() > 0;
//			if (hasChildren) {
//				return true;
//			}
//		}
//		return false;
	}

    /**
	 * @see remato.client.model.facade.DNDTarget#validTransfer(remato.client.model.ClientModelItem)
	 */
	@SuppressWarnings("unchecked")
	public boolean validTransfer(IPropertiesContainer type) {
		INodeParent parent = this;
		do {
			if (parent.equals(type)) {
				return false;
			}
			parent = parent.getParent();
		} while (parent != null);
		return true;
	}

}
